/*
 * =====================================================================================
 *
 *       Filename:  server.cpp
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  17.05.2012 20:53:48
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */
#include "server.hpp"

using namespace std::placeholders;

namespace Drosera {

ev::default_loop Server::m_loop;

/*
 * Stop the ev loop and the server
 */

void Server::stop ( ev::sig &signal, int revents ) {
	signal.loop.break_loop();
}

void Server::stop ( void ){
	m_io.stop();
	m_sio.stop();
}

Server::Server ( const char* path ) {
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	info ( "Protobuf Version is valid!" );
	if ( !readConfig ( path ))
		error ( "Cant parse config!" );

	// Init Servers

	if ( !initSocket ( getCertPub().c_str(), getCertPrv().c_str() ) )
		error ( "Cant init SSL-Socket" );

	else info ( "SSL-Socket initialized." );


}

/*
 * Runs the loop back for all Server
 */

 void Server::run ( void ) {
 	info ( "Lopp is running - waiting for new Clients." );
	m_loop.run();
 }


/*
 * Init the Server and alls his instances
 */

bool Server::init ( void ) {

	// Create a Socket that will be used for listening

	if ( !createSocket() ) {
		error ( "Can't create SSL-Socket!" );
		return false;
	}
	else info ( "SSL-Socket successfull created." );
	

	// Bind Socket to any Adress and Port 4433 (ssl)

	if ( !bindSocket( getPort() ) ) {
		warning ( "Can't bind SSL-Socket!" );
		return false;
	}
	else info ( "SSL-Socket successfull binded." );
	

	// Set Socket non blockable

	fcntl ( getSocket(), F_SETFL, fcntl ( getSocket(), F_GETFL, 0 ) | O_NONBLOCK );
	

	// Set the Socket to listening-mode

	if ( !listenSocket() ) {
		warning ( "Can't listen on SSL-Socket!" );
		return false;
	}
	else info ( "Listening on SSL-Socket." );
	


	// Set the EV-listener to the Sockets

	m_io.set <Server, &Server::incomingSocket> ( this );
	m_io.start ( getSocket(), ev::READ );

	m_sio.set <&Server::stop> ( );
	m_sio.start ( SIGINT );
	info ( "Set The EV-Listener for SSLSock" );
	return true;
	
}

/*
 * Method that get called, when an incomming connection is comming
 */

void Server::incomingSocket ( ev::io &watcher, int revents ) {

	// Check if the incomming event is valid 

	if ( EV_ERROR & revents ) error ( "Got invalid event" );

	// Run the Accept method to get the new incomming Client
	// and check for valid handshake

	SSL *ssl = SSL_new ( getCTX() );
	
	if ( sslacceptSocket ( watcher.fd, ssl ) ) {
		SOCKET fd;
		warning ( "Bad SSL-Handshake" );
		fd = SSL_get_fd ( ssl );
		closesocket ( fd );
		SSL_free ( ssl );
	} else {
		info ( "SSL-Handshake - OK" );
		int id;
		int ret = getId ( ssl, id );
		/*
		 * Return Values of getUID
		 * 0. Client already has an UID - start File Transfer
		 * -1. Client is broken - do nothing anymore
		 * 1. New incomming Client, create Client Object
		 */
		if ( ret == 0 ) {
			std::shared_ptr<Ftp> ptr ( new Ftp ( ssl ) );
			ptr->signalDisconnected().connect ( std::bind ( &Server::deleteFtp, this, _1, _2 ) );
			m_ftp.push_back ( std::move ( ptr ));
			info ( "New incomming FTP-Client" );
			return;
		} else if ( ret == -1 ) {
			warning ( "SSL-Client is Broken!" );
			SOCKET fd;
			fd = SSL_get_fd ( ssl );
			closesocket ( fd );
			SSL_free ( ssl );
			return;
		} else if ( ret == 1 ) {
			std::shared_ptr<Client> ptr ( new Client ( ssl ) );
			ptr->signalDisconnected().connect ( std::bind ( &Server::deleteClient, this, _1 ) );
			m_client.push_back ( std::move ( ptr ));
			info ( "New incomming Client (SSL)" );
		}
	}
}

/*
 * get the client id from the new incomming client
 * if no client id is set, so its a new client
 * otherwise it could be a filetransfer
 */

int Server::getId ( SSL *ssl, int& id ) {

	char buf[MAXRECV+1];
	int a;

	// get the informations from the side
	if ( ( a = SSL_read ( ssl, ( unsigned char*) buf, MAXRECV )) <= 0 )
		return -1;

	std::stringstream ss ( std::ios::in | std::ios::out | std::ios::binary );
	ss.write ( buf, a );

	// convert the raw bytes into the request
	Drosera::Request autho;
	if ( !autho.ParseFromIstream ( &ss ) )
		return -1;

	// check if this is a new client
	if ( autho.prototype() == Drosera::Request::SEND ||
		autho.reqtype() == Drosera::Request::AUTHORIZE  ) {
		id = autho.id();
		return 1;
	}

	// check if this is an ftp incomming transfer
	if ( autho.prototype() == Drosera::Request::SEND ||
		autho.reqtype() == Drosera::Request::FILE  ) {
		id = autho.id();
		return 0;
	}

	// corrupt incomming client
	return -1;
}



void Server::deleteClient ( unsigned int id ) {
	std::vector<std::shared_ptr<Client> >::iterator it;
	for ( it = m_client.begin(); it < m_client.end(); it++ ) {
		std::shared_ptr<Client> cli = *it;
		if ( id == cli->getUserId() ){
			m_signalDelClient( cli );
			m_client.erase( it );
			break;
		}
	}
	
}

void Server::deleteFtp ( unsigned int id, int error ) {
	std::vector<std::shared_ptr<Ftp> >::iterator it;
	for ( it = m_ftp.begin(); it < m_ftp.end(); it++ ) {
		std::shared_ptr<Ftp> ftp = *it;
		if ( id == ftp->getFtpId() ){
			m_signalDelFtp ( ftp, error );
			m_ftp.erase( it );
			break;
		}
	}
	
}

unsigned int Server::size ( void ) {
	return m_client.size();
}

Signal<void,std::shared_ptr<Client>>& Server::signalNewClient ( void ) {
	return m_signalNewClient;
}

Signal<void,std::shared_ptr<Client>>& Server::signalDelClient ( void ) {
	return m_signalDelClient;
}

Signal<void,std::shared_ptr<Ftp>>& Server::signalNewFtp ( void ) {
	return m_signalNewFtp;
}

Signal<void,std::shared_ptr<Ftp>,int>& Server::signalDelFtp ( void ) {
	return m_signalDelFtp;
}

Server::~Server ( void ) {
	google::protobuf::ShutdownProtobufLibrary();
}

}
